#ifndef _BUOLA_CV_FEATURES_CDETECTOR_H
#define _BUOLA_CV_FEATURES_CDETECTOR_H

#include <buola/cv/features/cfeatureset.h>
#include <buola/image/algorithm/detail/opencv.h>
#include <buola/mat.h>

#include <opencv2/nonfree/features2d.hpp>

namespace buola { namespace cvi {

template<typename tDetector>
class CDetector
{
public:
    template<typename... tArgs>
    CDetector(tArgs&&... pArgs)
        :   mDetector(std::forward<tArgs>(pArgs)...)
    {}
    
    std::vector<CKeyPoint> Detect(const img::CView_gray8 &pImage,const img::CView_gray8 &pMask,mat::CMat_f &pFeatures);
    std::vector<CKeyPoint> Detect(const img::CView_gray8 &pImage,mat::CMat_f &pFeatures);
    CFeatureSet Detect(const img::CView_gray8 &pImage);
    CFeatureSet Detect(const img::CView_gray8 &pImage,const img::CView_gray8 &pMask);

private:
    tDetector mDetector;
};

static std::vector<CKeyPoint> convert_keypoints(const std::vector<cv::KeyPoint> &pKeyPoints)
{
    std::vector<CKeyPoint> lReturn;
    lReturn.reserve(pKeyPoints.size());
    
    for(const cv::KeyPoint &k : pKeyPoints)
        lReturn.emplace_back(k);
    
    return lReturn;
}

template<typename tDetector>
std::vector<CKeyPoint> CDetector<tDetector>::Detect(const img::CView_gray8 &pImage,const img::CView_gray8 &pMask,
                                                    mat::CMat_f &pFeatures)
{
    std::vector<cv::KeyPoint> lKeyPoints;
    
    mDetector(mat_wrap(pImage),mat_wrap(pMask),lKeyPoints,mat_wrap(pFeatures));
    
    return convert_keypoints(lKeyPoints);
}

template<typename tDetector>
std::vector<CKeyPoint> CDetector<tDetector>::Detect(const img::CView_gray8 &pImage,mat::CMat_f &pFeatures)
{
    std::vector<cv::KeyPoint> lKeyPoints;
    
    mDetector(mat_wrap(pImage),cv::noArray(),lKeyPoints,mat_wrap(pFeatures));
    
    return convert_keypoints(lKeyPoints);
}

template<typename tDetector>
CFeatureSet CDetector<tDetector>::Detect(const img::CView_gray8& pImage)
{
    mat::CMat_f lFeatures;
    std::vector<CKeyPoint> lPoints=Detect(pImage,lFeatures);
    
    return CFeatureSet(std::move(lPoints),std::move(lFeatures));
}

template<typename tDetector>
CFeatureSet CDetector<tDetector>::Detect(const img::CView_gray8& pImage,const img::CView_gray8 &pMask)
{
    mat::CMat_f lFeatures;
    std::vector<CKeyPoint> lPoints=Detect(pImage,pMask,lFeatures);
    
    return CFeatureSet(std::move(lPoints),std::move(lFeatures));
}

typedef CDetector<cv::SURF> CSURFDetector;
typedef CDetector<cv::SIFT> CSIFTDetector;

/*namespace cvi*/ } /*namespace buola*/ }

#endif
